R
Ridey 骑益 · 会员中心
} function lookupMember(phone) { if (!MEMBER_DB) return null; for (var i = 0; i < MEMBER_DB.length; i++) { if (MEMBER_DB[i].p === phone) { return { name: MEMBER_DB[i].n || '', nickname: '', email: '', wechat: MEMBER_DB[i].w || '', membership: MEMBER_DB[i].m || '非会员', membershipStart: MEMBER_DB[i].ms || '', membershipEnd: MEMBER_DB[i].me || '', inseam: '', bikeHeight: '', saddleForeAft: '', handlebarHeight: '', handlebarForeAft: '', dataComplete: true, phone: phone }; } } return null; } // ========================================== // Load Maintenance Records // ========================================== async function loadMemberData(phone) { maintenanceRecords = []; if (dbReady) { try { var result = await db.collection(MAINTENANCE_COLLECTION) .where({ 'customer.phone': phone }) .orderBy('createdAt', 'desc') .limit(50) .get(); if (result.data && result.data.length > 0) { maintenanceRecords = result.data; } // Also try to enrich member info from CloudBase customers collection try { var custResult = await db.collection(CUSTOMERS_COLLECTION) .where({ phone: phone }) .limit(1) .get(); if (custResult.data && custResult.data.length > 0) { var c = custResult.data[0]; if (c.name && !currentMember.name) currentMember.name = c.name; if (c.membership && c.membership !== 'non-member') currentMember.membership = c.membership; if (c.membershipStart) currentMember.membershipStart = c.membershipStart; if (c.membershipEnd) currentMember.membershipEnd = c.membershipEnd; if (c.nickname) currentMember.nickname = c.nickname; if (c.email) currentMember.email = c.email; if (c.inseam) currentMember.inseam = c.inseam; if (c.bikeHeight) currentMember.bikeHeight = c.bikeHeight; if (c.saddleForeAft) currentMember.saddleForeAft = c.saddleForeAft; if (c.handlebarHeight) currentMember.handlebarHeight = c.handlebarHeight; if (c.handlebarForeAft) currentMember.handlebarForeAft = c.handlebarForeAft; // 自动补全:有开始但无截止,+6个月-1天 if (currentMember.membershipStart && !currentMember.membershipEnd && currentMember.membership === '会员') { var dd_auto2 = new Date(currentMember.membershipStart); if (!isNaN(dd_auto2.getTime())) { dd_auto2.setMonth(dd_auto2.getMonth() + 6); dd_auto2.setDate(dd_auto2.getDate() - 1); var y2 = dd_auto2.getFullYear(); var m2 = String(dd_auto2.getMonth() + 1).padStart(2, '0'); var d2 = String(dd_auto2.getDate()).padStart(2, '0'); currentMember.membershipEnd = y2 + '-' + m2 + '-' + d2; } } } } catch (e) { console.warn('Customer enrichment failed:', e); } } catch (e) { console.warn('Failed to load records from CloudBase:', e.message); } } // If no records found from CloudBase, show empty state return maintenanceRecords; } // ========================================== // Dashboard Render // ========================================== function renderDashboard() { var m = currentMember; var records = maintenanceRecords; // Stats var totalMaintenance = records.length; var lastMaintenance = records.length > 0 ? formatDate(records[0].createdAt) : '暂无'; var memberType = m.membership || '非会员'; var isCloudProfile = m._fromCloud; // Membership badge class var badgeClass = 'badge-non-member'; if (memberType === '会员') badgeClass = 'badge-member'; else if (memberType === '优惠') badgeClass = 'badge-discount'; var membershipInfo = ''; if (memberType === '会员' && (m.membershipStart || m.membershipEnd)) { membershipInfo = '
' + (m.membershipStart ? formatDate(m.membershipStart) : '') + (m.membershipEnd ? ' ~ ' + formatDate(m.membershipEnd) : '') + '
'; } var html = ''; // CloudBase status indicator if (!dbReady) { html += '
'; html += '⚠️ 云端连接未就绪,仅显示本地会员信息,保养记录可能不完整'; html += '
'; } // 不完整档案提示(保养/装车系统录入的新客户,信息待补全) if (m.dataComplete === false || (m._fromCloud && !m.nickname && !m.email && !m.wechat && !m.inseam)) { html += '
'; html += 'ℹ️ 您的会员档案信息尚不完整,部分功能受限。请联系管理员完善资料(昵称、邮箱、Bike Fitting 数据等)'; html += '
'; } // Profile Card html += '
'; html += '' + memberType + ''; if (isCloudProfile) { html += '云端匹配'; } html += '
' + escHtml(m.name || '骑友') + '
'; // 曾用名展示 if (m.aliases && m.aliases.length > 0) { html += '
又名:' + m.aliases.map(escHtml).join('、') + '
'; } html += '
' + m.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') + '
'; html += '
'; if (m.nickname) { html += '
昵称' + escHtml(m.nickname) + '
'; } if (m.wechat) { html += '
微信' + escHtml(m.wechat) + '
'; } if (m.email) { html += '
邮箱' + escHtml(m.email) + '
'; } html += '
'; // Bike Fitting 数据 var hasFitting = m.inseam || m.bikeHeight || m.saddleForeAft || m.handlebarHeight || m.handlebarForeAft; if (hasFitting) { html += '
'; html += '
Bike Fitting
'; html += '
'; if (m.inseam) { html += '
胯高' + escHtml(m.inseam) + ' cm
'; } if (m.bikeHeight) { html += '
单车高度' + escHtml(m.bikeHeight) + ' cm
'; } if (m.saddleForeAft) { html += '
坐垫前后' + escHtml(m.saddleForeAft) + ' cm
'; } if (m.handlebarHeight) { html += '
车把高度' + escHtml(m.handlebarHeight) + ' cm
'; } if (m.handlebarForeAft) { html += '
车把前后' + escHtml(m.handlebarForeAft) + ' cm
'; } html += '
'; } html += membershipInfo; html += '
'; // Stats Row html += '
'; html += '
' + totalMaintenance + '
保养次数
'; html += '
' + (hasFitting ? '✓' : '-') + '
Bike Fitting
'; html += '
' + (memberType === '会员' ? '✓' : '-') + '
会员状态
'; html += '
'; // Maintenance History html += '
'; html += '

保养历史记录

'; html += '共 ' + totalMaintenance + ' 条'; html += '
'; html += '
'; if (records.length === 0) { html += '
'; html += '
🔧
'; html += '
暂无保养记录
完成首次保养后将在此显示
'; html += '
'; } else { for (var i = 0; i < records.length; i++) { var r = records[i]; html += renderRecordCard(r, i); } } html += '
'; document.getElementById('dashboardContent').innerHTML = html; } // ========================================== // Record Card // ========================================== function renderRecordCard(record, index) { var typeLabel = getMaintenanceTypeLabel(record); var dateStr = record.createdAt ? formatDate(record.createdAt) : ''; var status = record.status || 'completed'; // Service items tags var tags = []; var items = record.serviceItems || []; for (var i = 0; i < Math.min(items.length, 4); i++) { tags.push(items[i].name); } var tagsHtml = ''; for (var j = 0; j < tags.length; j++) { tagsHtml += '' + escHtml(tags[j]) + ''; } if ((record.serviceItems || []).length > 4) { tagsHtml += '+' + (record.serviceItems.length - 4) + ''; } var statusClass = status === 'completed' ? 'status-completed' : 'status-pending'; var statusLabel = status === 'completed' ? '已完成' : '进行中'; return '
' + '
' + '
' + escHtml(typeLabel) + '
' + '
' + dateStr + '
' + '
' + (tagsHtml ? '
' + tagsHtml + '
' : '') + '
' + '' + statusLabel + '' + '查看详情 ' + '
' + '
'; } // ========================================== // Detail Modal // ========================================== function showDetail(index) { var record = maintenanceRecords[index]; if (!record) return; var pathLabel = { maintenance: '整车保养', repair: '维修', wash: '清洗', assembly: '装车' }; var paths = record.selectedPaths || {}; var pathNames = []; for (var k in paths) { if (paths[k]) pathNames.push(pathLabel[k] || k); } var typeLabel = pathNames.length > 0 ? pathNames.join(' · ') : '保养记录'; var dateStr = record.createdAt ? formatDateTime(record.createdAt) : ''; var status = record.status || 'completed'; var statusLabel = status === 'completed' ? '已完成' : '进行中'; var statusIcon = status === 'completed' ? '✅' : '🔧'; // Customer info var customer = record.customer || {}; var html = ''; // modal-overlay document.getElementById('detailModal').innerHTML = html; document.getElementById('detailModal').style.display = 'block'; document.body.style.overflow = 'hidden'; } function closeDetail(e) { if (e && e.target !== e.currentTarget) return; document.getElementById('detailModal').style.display = 'none'; document.getElementById('detailModal').innerHTML = ''; document.body.style.overflow = ''; } // ========================================== // Photo Viewer // ========================================== function viewPhoto(src) { var viewer = document.getElementById('photoViewer'); viewer.innerHTML = '点击关闭'; viewer.style.display = 'flex'; document.body.style.overflow = 'hidden'; } function closePhotoViewer() { document.getElementById('photoViewer').style.display = 'none'; document.getElementById('photoViewer').innerHTML = ''; document.body.style.overflow = ''; } // ========================================== // Logout // ========================================== function handleLogout() { currentMember = null; maintenanceRecords = []; showView('Login'); document.getElementById('phoneInput').value = ''; document.getElementById('loginError').textContent = ''; } // ========================================== // Helpers // ========================================== function getMaintenanceTypeLabel(record) { var paths = record.selectedPaths || {}; var labels = []; if (paths.maintenance) labels.push('整车保养'); if (paths.repair) labels.push('维修'); if (paths.wash) labels.push('清洗'); if (paths.assembly) labels.push('装车'); if (labels.length > 0) return labels.join(' · '); return record.path || '保养记录'; } function formatDate(dateStr) { if (!dateStr) return ''; try { var d = new Date(dateStr); return d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate(); } catch(e) { return dateStr; } } function formatDateTime(dateStr) { if (!dateStr) return ''; try { var d = new Date(dateStr); return d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate() + ' ' + String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0'); } catch(e) { return dateStr; } } function escHtml(str) { if (!str) return ''; return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function escAttr(str) { if (!str) return ''; return String(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>'); } // ========================================== // Keyboard: ESC to close modals // ========================================== document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { if (document.getElementById('detailModal').style.display === 'block') { closeDetail({ target: document.querySelector('.modal-overlay') }); } if (document.getElementById('photoViewer').style.display === 'flex') { closePhotoViewer(); } } }); // ========================================== // Init // ========================================== (async function() { // MEMBER_DB is injected inline at build time initCloudBase(); })();